package com.zzyl.nursing.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.nursing.config.WebSocketServer;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertRuleService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.system.mapper.SysUserRoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 报警规则Service业务层处理
 * 
 * @author zzyl
 * @date 2025-10-27
 */
@Slf4j
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService
{
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    /**
     * 查询报警规则
     * 
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     * 
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule)
    {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule)
    {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule)
    {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     * 
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     * 
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id)
    {
        return removeById(id) ? 1 : 0;
    }

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private DeviceMapper deviceMapper;
    @Value("${alertRule.roleAdmin}")
    private String roleAdmin;
    @Value("${alertRule.roleRepair}")
    private String roleRepair;
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    @Autowired
    private IAlertDataService alertDataService;
    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 报警数据过滤，并批量保存报警数据
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void alertRuleFilter() {

        // 1、获取所有报警规则
        Long alertRuleCount = this.lambdaQuery().eq(AlertRule::getStatus, 1).count();
        if (alertRuleCount <= 0) {
            log.error("[定时报警规则校验]没有任何报警规则，无需处理");
            return;
        }

        // 2、查询所有设备上报的数据
        List<Object> values = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_DATA_LATEST);
        if (CollUtil.isEmpty(values)) {
            log.error("[定时报警规则校验]没有任何设备上报的数据，无需处理");
            return;
        }

        // 3、数据格式标准化处理
        List<DeviceData> deviceDataAllList = new ArrayList<>();
        for (Object value : values) {
            List<DeviceData> deviceDataList = JSONUtil.toList((String) value, DeviceData.class);
            deviceDataAllList.addAll(deviceDataList);
        }

        // 4、获取到全部的设备数据，进行过滤处理
        for (DeviceData deviceData : deviceDataAllList) {
            // 4.1、校验设备上报的时间是否太久
            int limit = 3600; // 单位为秒，超过1小时的设备数据不再处理
            // 获取间隔时间
            long intervalTime = Math.abs(LocalDateTimeUtil.between(deviceData.getAlarmTime(), LocalDateTime.now(), ChronoUnit.SECONDS));
            if (intervalTime > limit) {
                log.error("[定时报警规则校验]设备上报数据的时间太久，已超过{}秒，不做处理", limit);
                continue;
            }

            // 4.2、查询上报数据对应的设备绑定的全部报警规则数据
            // 4.2.1、查询当前上报数据对应的产品类型下同样的功能绑定的全部规则数据（所有设备）
            List<AlertRule> productAlertRuleList = this.lambdaQuery()
                    .eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getIotId, -1)
                    .eq(AlertRule::getStatus, 1).list();

            // 4.2.2、查询当前上报数据对应的设备下绑定的规则数据（具体设备）
            List<AlertRule> deviceAlertRuleList = this.lambdaQuery().eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getIotId, deviceData.getIotId())
                    .eq(AlertRule::getStatus, 1).list();

            // 4.2.3、合并全部报警规则数据
            Set<AlertRule> alertRuleSet = CollUtil.unionDistinct(productAlertRuleList, deviceAlertRuleList);
            if (CollUtil.isEmpty(alertRuleSet)) {
                log.error("[定时报警规则校验]没有报警规则数据，无需处理");
                continue;
            }

            // 4.3、遍历每一条规则
            for (AlertRule alertRule : alertRuleSet) {
                // 4.3.1、第一层校验：判断生效时间
                String period = alertRule.getAlertEffectivePeriod();
                List<String> periodList = Arrays.asList(period.split("~"));
                LocalTime startTime = LocalTime.parse(periodList.get(0));
                LocalTime endTime = LocalTime.parse(periodList.get(1));
                LocalTime alarmTime = deviceData.getAlarmTime().toLocalTime();

                if (alarmTime.isBefore(startTime) || alarmTime.isAfter(endTime)) {
                    log.error("[定时报警规则校验]设备名：{}，功能名，{}，设备id：{}，规则名：{}，数据上报时间不在生效时间段内，不做处理", deviceData.getDeviceName(), alertRule.getFunctionName(), deviceData.getIotId(), alertRule.getAlertRuleName());
                    continue;
                }

                // 从redis中获取报警次数的缓存key
                String countKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_COUNT_KEY, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());

                // 4.3.2、第二层校验：判断数据阈值
                double value = alertRule.getValue();
                String operator = alertRule.getOperator();
                double dataValue = Double.valueOf(deviceData.getDataValue());
                if ("<".equals(operator) && dataValue >= value || ">=".equals(operator) && dataValue < value) {
                    log.error("[定时报警规则校验]设备名：{}，功能名，{}，设备id：{}，规则名：{}，设备上报的数据符合数据阈值范围内，不做处理", deviceData.getDeviceName(), alertRule.getFunctionName(), deviceData.getIotId(), alertRule.getAlertRuleName());
                    // 删除redis报警次数
                    redisTemplate.delete(countKey);
                    continue;
                }

                // 4.3.3、第三层校验：判断沉默周期
                String silentKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_SILENT_KEY, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());
                String silentValue = redisTemplate.opsForValue().get(silentKey);
                // 如果获取到的沉默周期时间不为空，说明还有计时，仍然在沉默周期中，不做处理
                if (StrUtil.isNotBlank(silentValue)) {
                    Long ttl = redisTemplate.getExpire(silentKey);
                    log.error("[定时报警规则校验]设备名：{}，功能名，{}，设备id：{}，设备名：{}，当前报警规则仍在沉默周期内，还剩{}秒", deviceData.getDeviceName(), alertRule.getFunctionName(), deviceData.getIotId(), alertRule.getAlertRuleName(), ttl);
                    continue;
                }

                // 4.3.4、第四层校验：判断持续周期
                Integer duration = alertRule.getDuration();
                Long durationCount = redisTemplate.opsForValue().increment(countKey);
                if (durationCount < duration) {
                    log.error("[定时报警规则校验]设备名：{}，功能名，{}，设备id：{}，设备名：{}，当前报警次数未达到持续周期上限，现在{}次，上限{}次", deviceData.getDeviceName(), alertRule.getFunctionName(), deviceData.getIotId(), alertRule.getAlertRuleName(), durationCount, duration);
                    continue;
                }

                // 添加redis沉默周期
                redisTemplate.opsForValue().set(silentKey, "silent", alertRule.getAlertSilentPeriod(), TimeUnit.MINUTES);

                // 删除redis报警次数
                redisTemplate.delete(countKey);

                // 4.3.5、校验全部通过，当前数据触发报警
                // 4.3.5.1、判断是否是老人数据
                List<Long> nursingIdList = new ArrayList<>();
                if (alertRule.getAlertDataType() == 0) { // 老人数据
                    // 4.3.5.2、判断是否是固定设备
                    if (deviceData.getLocationType() == 0) { // 随身设备
                        nursingIdList = deviceMapper.selectNuringIdsByMoveIotId(deviceData.getIotId());
                    } else {
                        nursingIdList = deviceMapper.selectNuringIdsByStopIotId(deviceData.getIotId());
                    }
                } else {
                    nursingIdList = sysUserRoleMapper.selectUserIdsByRoleName(roleRepair);
                }

                // 4.3.5.3、查询超级管理员
                List<Long> adminIdList = sysUserRoleMapper.selectUserIdsByRoleName(roleAdmin);

                // 合并所有的用户id
                Set<Long> userIdList = CollUtil.unionDistinct(nursingIdList, adminIdList);
                if (CollUtil.isEmpty(userIdList)) {
                    log.error("[定时报警规则校验]没有查询任何用户id，设备id：{}",  deviceData.getIotId());
                    continue;
                }
                
                // 构建报警数据
                // 获取报警数据的基本信息对象
                AlertData baseData = BeanUtil.toBean(deviceData, AlertData.class);
                baseData.setAlertRuleId(alertRule.getId()); // 报警规则id
                String alertReasonTemplate = "功能：{}{}{}，持续周期：{}，沉默周期：{}";
                String alertReason = CharSequenceUtil.format(alertReasonTemplate, alertRule.getFunctionName(), alertRule.getOperator(),alertRule.getValue(), alertRule.getDuration(), alertRule.getAlertSilentPeriod());
                baseData.setAlertReason(alertReason); // 报警原因
                baseData.setStatus(0); // 待处理状态
                baseData.setId(null);
                baseData.setType(alertRule.getAlertDataType()); // 报警数据类型

                // 构建每一个用户的报警数据
                List<AlertData> alertDataList = userIdList.stream().map(x -> {
                    AlertData alertData = BeanUtil.toBean(baseData, AlertData.class);
                    alertData.setUserId(x);
                    return alertData;
                }).collect(Collectors.toList());

                // 4.3.5.4、批量保存报警数据
                alertDataService.saveBatch(alertDataList);
                log.info("[定时报警规则校验]新增报警数据完毕，设备id：{}，报警信息：{}", deviceData.getIotId(), alertReason);

                // 4.3.5.5、报警通知
                userIdList.forEach(userId -> {
                    AlertNotifyVo notifyVo = BeanUtil.toBean(deviceData, AlertNotifyVo.class);
                    notifyVo.setFunctionName(alertRule.getFunctionName());
                    notifyVo.setAlertDataType(alertRule.getAlertDataType());
                    notifyVo.setNotifyType(1);

                    webSocketServer.notifyMsgToClient(String.valueOf(userId), JSONUtil.toJsonStr(notifyVo));
                });
            }
        }
    }
}
